/**
* @file retr_init.cpp
*
* Copyright (C) Huawei Technologies Co., Ltd. 2019-2020. All Rights Reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include "retr_init.h"

#include <securec.h>
#include "aicpu/common/aicpu_task_struct.h"

#include "retr_internal.h"

namespace acl {
namespace retr {
namespace {
constexpr const char_t *RETR_KERNELNAME_INITIALIZE = "RetrInitializeKernel";
}  // namespace

/**
 * Check parameters and construct the input parameters of the rtCpuKernelLaunchWithFlag
 * @return ACL_SUCCESS:success other:failed
 */
aclError AclFvInit::PrepareInput(const aclfvInitPara *const initPara, char_t *const args, const uint32_t argsSize)
{
    aclError ret = aclrtGetRunMode(&runMode_);
    if (ret != ACL_SUCCESS) {
        ACL_LOG_INNER_ERROR("[Get][RunMode]aclrtGetRunMode failed, ret = %d", ret);
        return ret;
    }

    const auto paramHead = reinterpret_cast<aicpu::AicpuParamHead *>(args);
    paramHead->length = argsSize;
    // RetrInitialize has 1 input/output aclfvInitPara
    paramHead->ioAddrNum = 1U;
    const auto ioAddr = reinterpret_cast<uint64_t *>(args + sizeof(aicpu::AicpuParamHead));

    ACL_REQUIRES_NOT_NULL(initPara->dataBuffer.data);
    const rtError_t rtRet = rtStreamCreate(&stream_, static_cast<int32_t>(RT_STREAM_PRIORITY_DEFAULT));
    if (rtRet != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Create][Stream]AclFvInit create stream failed, runtime errorCode = %d", rtRet);
        return ACL_ERROR_RT_FAILURE;
    }

    ioAddr[0] = reinterpret_cast<uintptr_t>(initPara->dataBuffer.data);

    aclrtMemcpyKind memcpyKind = ACL_MEMCPY_DEVICE_TO_DEVICE;
    if (runMode_ == ACL_HOST) {
        memcpyKind = ACL_MEMCPY_HOST_TO_DEVICE;
    }
    ret = aclrtMemcpyAsync(initPara->dataBuffer.data, initPara->dataBuffer.length,
        &(initPara->initPara), sizeof(initPara->initPara), memcpyKind, stream_);
    if (ret != ACL_SUCCESS) {
        ACL_LOG_INNER_ERROR("[Copy][Mem]memcpy between host and device failed, kind = %d, errorCode = %d",
            static_cast<int32_t>(memcpyKind), ret);
        (void)rtStreamDestroy(stream_);
        return ret;
    }
    return ACL_SUCCESS;
}

/**
 * execute retr init task, including check parameters launch task and check result
 * @return ACL_SUCCESS:success other:failed
 */
aclError AclFvInit::Init(aclfvInitPara *const initPara)
{
    ACL_LOG_INFO("start to execute aclfvInit.");
    ACL_REQUIRES_NOT_NULL(initPara);
    // aclfvInit has 1 input/output aclfvInitPara
    constexpr const uint32_t ioAddrNum = 1U;
    constexpr const size_t argsSize = sizeof(aicpu::AicpuParamHead) +
        (static_cast<size_t>(ioAddrNum) * sizeof(uint64_t));
    char_t args[argsSize] = {};
    ACL_REQUIRES_OK(PrepareInput(initPara, args, argsSize));

    ACL_LOG_INFO("rtCpuKernelLaunchWithFlag %s begin.", RETR_KERNELNAME_INITIALIZE);
    // blockDim default 1, no need smDesc
    rtArgsEx_t argsInfo = {};
    argsInfo.args = args;
    argsInfo.argsSize = static_cast<uint32_t>(argsSize);
    rtError_t rtRet = rtCpuKernelLaunchWithFlag(RETR_KERNELS_SONAME, RETR_KERNELNAME_INITIALIZE, 1U, &argsInfo,
        nullptr, stream_, RT_KERNEL_DEFAULT);
    if (rtRet != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Initialize][Repr]retr initialize call rtCpuKernelLaunchWithFlag failed, "
            "runtime errorCode = %d", rtRet);
        (void)rtStreamDestroy(stream_);
        return ACL_ERROR_RT_FAILURE;
    }
    ACL_LOG_INFO("rtCpuKernelLaunchWithFlag %s success.", RETR_KERNELNAME_INITIALIZE);

    aclrtMemcpyKind memcpyKind = ACL_MEMCPY_DEVICE_TO_DEVICE;
    if (runMode_ == ACL_HOST) {
        memcpyKind = ACL_MEMCPY_DEVICE_TO_HOST;
    }
    aclError ret = aclrtMemcpyAsync(&(initPara->initPara), sizeof(initPara->initPara),
        initPara->dataBuffer.data, initPara->dataBuffer.length, memcpyKind, stream_);
    if (ret != ACL_SUCCESS) {
        ACL_LOG_INNER_ERROR("[Copy][Mem]memcpy between host and device failed, kind = %d, errorCode = %d",
            static_cast<int32_t>(memcpyKind), ret);
        (void)rtStreamDestroy(stream_);
        return ret;
    }

    rtRet = rtStreamSynchronize(stream_);
    if (rtRet != RT_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[Sync][Mem]synchronize stream failed, runtime errorCode = %d", rtRet);
        (void)rtStreamDestroy(stream_);
        return ACL_ERROR_RT_FAILURE;
    }
    (void)rtStreamDestroy(stream_);

    if (initPara->initPara.retCode != 0) {
        ACL_LOG_INNER_ERROR("[Init][Fv]aclfvInit failed, ret = %d", initPara->initPara.retCode);
        return ACL_ERROR_FAILURE;
    }
    ACL_LOG_INFO("execute aclfvInit success.");
    return ACL_SUCCESS;
}
}  // namespace retr
}  // namespace acl
